/*
* Script for dealing with the input and drawing the L-System.
*/

(function () {
	var canvas = document.getElementById("breakerCanvas"),
		width = canvas.width,
		height = canvas.height,
		ctx = canvas.getContext("2d"),
		
		uiHeight = height / 20,
		currentScore = 0,
		currentLives = 3,
		
		paddleHeight = height / 30,
		paddleWidth = width / 8,
		paddleY = height - (uiHeight + (1/2) * paddleHeight),
		paddleOffset = 0,
		paddleSpeed = 20,
		PADDLE_LEFT = 97, // 'a' character code
        PADDLE_RIGHT = 100, // 'd' character code
        PADDLE_MAX = width - paddleWidth,
		
		ballLaunched = false,
		ballSpeed = 7,
		ballAngle = -Math.PI / 3,
		ballRadius = paddleHeight / 2,
		ballStartY = paddleY - ballRadius,
		ballStartX = paddleWidth / 2,
		ballPosition = [ballStartX, ballStartY],
		BALL_LAUNCH = 119, // 'w' character code
		
		BRICK_WIDTH = paddleWidth / 3,
		BRICK_HEIGHT = paddleHeight,
		BRICK_COLUMNS = (width - paddleWidth) / BRICK_WIDTH,
		START_BRICK_LAYING = [(width - BRICK_COLUMNS * BRICK_WIDTH) / 2, height / 5],
		BRICK_ROWS = 7,
		brickCoords = [],
		brickPresence = [],
		
		killsLeft = BRICK_COLUMNS * BRICK_ROWS,
		
		/**
		* Draws the static elements of the game. These include:
		* the dangerzone at the bottom and the score
		*/
		drawStaticElements = function() {
			// Begin with the dangerzone
			ctx.save();
				ctx.beginPath();
				    ctx.moveTo(0, height);
					ctx.lineTo(0, height - uiHeight);
					ctx.lineTo(width, height - uiHeight);
					ctx.lineTo(width, height);
				ctx.closePath();
				ctx.fillStyle = "rgb(255, 0, 0)";
				ctx.fill();
			
			    // Add the score bar
			    ctx.beginPath();
                    ctx.moveTo(0, 0);
                    ctx.lineTo(width, 0);
                    ctx.lineTo(width, uiHeight);
                    ctx.lineTo(0, uiHeight);
                ctx.closePath();
                ctx.fillStyle = "rgb(0, 0, 0)";
                ctx.fill();
			
			    // Then, the score text
				ctx.font = "20px Arial";
	            ctx.fillStyle = "rgb(255, 255, 255)";
	            ctx.fillText("SCORE: " + currentScore, width / 100, height / 30);
				
				// Lastly, the number of lives
                ctx.font = "20px Arial";
                ctx.fillStyle = "rgb(255, 255, 255)";
                ctx.fillText("LIVES: " + currentLives, width * 0.88, height / 30);
			ctx.restore();
		},
		
		/**
		 * Draws the "pong paddle" at its current position
		 */
		drawPaddle = function() {
			ctx.save();
			ctx.translate(paddleOffset, paddleY);
			    ctx.beginPath();
				    ctx.moveTo(0, 0);
					ctx.lineTo(0, paddleHeight);
					ctx.lineTo(paddleWidth, paddleHeight);
					ctx.lineTo(paddleWidth, 0);
				ctx.closePath();
				ctx.fillStyle = "rgb(0, 0, 255)";
				ctx.fill();
			ctx.restore();
		},
		
		/**
		 * Draws the ball at its current position
		 */
		drawBall = function() {
			// Calculate the ball position before moving
			if (ballLaunched) {
				ballPosition[0] += ballSpeed * Math.cos(ballAngle);
				ballPosition[1] += ballSpeed * Math.sin(ballAngle);
				
				// Check for collisions, first with the outside bound
				if (ballPosition[0] < 0) {
					ballPosition[0] = 0;
					ballAngle = Math.PI - ballAngle;
				} else if(ballPosition[0] > width - ballRadius) {
					ballPosition[0] = width - ballRadius;
					ballAngle = Math.PI - ballAngle;
				}
				
				if (ballPosition[1] < 0) {
					ballPosition[1] = 0;
					ballAngle = -ballAngle;
				}
				
				// Check for collision with the paddle
				if (ballPosition[1] > paddleY) {
					loseOrWin(0);
					return;
				}
				
				if (ballPosition[1] >= ballStartY) {
					if (ballPosition[0] >= paddleOffset && ballPosition[0] <= paddleOffset + paddleWidth) {
						ballAngle = -ballAngle;
						currentScore -= 1; // Better scores for killing blocks faster
					}
				}
				
				// Normalize the angle
				if (ballAngle >= Math.PI) {
					ballAngle -= 2 * Math.PI;
				}
				
				if (ballAngle < -Math.PI) {
					ballAngle += 2 * Math.PI;
				}
				// Collisions with blocks are handled in drawBrickGrid()
			}
			
			ctx.save();
            ctx.translate(ballPosition[0], ballPosition[1]);
                ctx.beginPath();
                    ctx.moveTo(0, 0);
					ctx.arc(0, 0, ballRadius, 0, 2 * Math.PI);
                ctx.closePath();
                ctx.fillStyle = "rgb(200, 200, 0)";
                ctx.fill();
            ctx.restore();
		},
		
		/**
		 * Drawing utility for the brick objects
		 */
		drawBrick = function(x, y) {
			ctx.save();
            ctx.translate(x, y);
                ctx.beginPath();
                    ctx.moveTo(0, 0);
                    ctx.lineTo(0, BRICK_HEIGHT);
					ctx.lineTo(BRICK_WIDTH, BRICK_HEIGHT);
					ctx.lineTo(BRICK_WIDTH, 0);
                ctx.closePath();
                ctx.fillStyle = "rgb(255, 0, 255)";
                ctx.fill();
            ctx.restore();
			ctx.stroke();
		},
		
		/**
		 * Drawing utility for drawing the grid of bricks
		 */
		drawBrickGrid = function() {
			var brickX, brickY, differentialX, differentialY, collisionFound = false, refractory = true;
			for (var i = 0; i < BRICK_COLUMNS; i++) {
                for (var j = 0; j < BRICK_ROWS; j++) {
					brickX = brickCoords[i][j][0] + START_BRICK_LAYING[0];
				    brickY = brickCoords[i][j][1] + START_BRICK_LAYING[1];
                    if (brickPresence[i][j]) {
						if (ballAngle <= 0) { // Ball headed upwards
							if (ballPosition[1] - ballRadius < brickY + BRICK_HEIGHT && ballPosition[1] - ballRadius > brickY
							    && ballPosition[0] > brickX && ballPosition[0] < brickX + BRICK_WIDTH) {
								    ballAngle = -ballAngle;
								    collisionFound = true;
							}
						} else { // Ball headed downwards
							if (ballPosition[1] + ballRadius > brickY && ballPosition[1] + ballRadius < brickY + BRICK_HEIGHT 
							    && ballPosition[0] > brickX && ballPosition[0] < brickX + BRICK_WIDTH && refractory) {
                                    ballAngle = -ballAngle;
                                    collisionFound = true;
									refractory = false;
                            }
						}
						if (ballAngle < Math.PI / 2 && ballAngle >= -Math.PI / 2) {
							if (ballPosition[1] + ballRadius > brickY && ballPosition[1] + ballRadius < brickY + BRICK_HEIGHT &&
							ballPosition[0] + ballRadius > brickX && ballPosition[0] < brickX + BRICK_WIDTH && refractory) {
							    ballAngle = Math.PI - ballAngle;
								collisionFound = true;
								refractory = false;
							}
						} else {
							if (ballPosition[1] + ballRadius > brickY && ballPosition[1] + ballRadius < brickY + BRICK_HEIGHT &&
							ballPosition[0] > brickX && ballPosition[0] - ballRadius < brickX + BRICK_WIDTH && refractory) {
								ballAngle = Math.PI - ballAngle;
                                collisionFound = true;
                                refractory = false;
							}
						}
						if (collisionFound) {
							refractory = false;
							collisionFound = false;
							currentScore += 20;
							brickPresence[i][j] = false;
						}
						refractory = true;
						drawBrick(brickX, brickY);
					}
                }
            }
		},
		
		/**
		 * Reset for having lost a life or winning
		 */
		loseOrWin = function(condition) {
			if (condition === 0) {
				// Subtract a life, ending the game if none are left
				currentLives--;
				if (currentLives <= 0) {
					alert("YOU HAZ DIEDEDEDED \n\nTEH SCOREZ: " + currentScore + "\n\nAGAIN?!?!");
					fullReset();
				}
			} else {
				alert("NOOOEEEZ! MY BLOXORZ!!! \n\nTEH SCOREZ: " + currentScore + "\n\nAGAIN?!?!");
				fullReset();
			}
			paddleOffset = 0;
			ballLaunched = false;
			ballPosition = [ballStartX, ballStartY];
			ballAngle = -Math.PI / 3;
		},
		
		/**
		 * Resets the stage and counters on win or lose
		 */
		fullReset = function() {
			for (var i = 0; i < BRICK_COLUMNS; i++) {
				for (var j = 0; j < BRICK_ROWS; j++) {
					brickPresence[i][j] = true;
				}
			}
			currentLives = 3;
            currentScore = 0;
		},
		
		/**
		 * Set up the event handling and variable population
		 */
		configureStage = function() {
			// Bind the key pressing to the canvas object
			$("body").keypress(keyPressed);
			
			// Populates the brick arrays
			for (var i = 0; i < BRICK_COLUMNS; i++) {
				brickCoords[i] = new Array();
				brickPresence[i] = new Array();
				for (var j = 0; j < BRICK_ROWS; j++) {
					brickCoords[i][j] = [BRICK_WIDTH * i, BRICK_HEIGHT * j];
					brickPresence[i][j] = true;
				}
			}
		},
		
		/**
		 * Handler for keyboard events
		 */
		keyPressed = function(event) {
			switch(event.which) {
				case PADDLE_LEFT: 
				    paddleOffset -= paddleSpeed;
					if (paddleOffset < 0) {
						paddleOffset = 0;
					}
					if (!ballLaunched) {
                        ballPosition[0] = paddleOffset + ballStartX;
                    }
				    break;
				case PADDLE_RIGHT: 
                    paddleOffset += paddleSpeed;
                    if (paddleOffset > PADDLE_MAX) {
                        paddleOffset = PADDLE_MAX;
                    }
					if (!ballLaunched) {
                        ballPosition[0] = paddleOffset + ballStartX;
                    }
					break;
				case BALL_LAUNCH:
				    ballLaunched = true;
				    break;
			}
		},
		
		/**
		 * Handles the animation, clearing the canvas and
		 * drawing its elements appropriately
		 */
		update = function() {
			ctx.clearRect(0, 0, width, height);
			drawStaticElements();
		    drawPaddle();
			drawBrickGrid();
		    drawBall();
			// Have we won yet?!?!?!?!
			if (killsLeft === 0) {
				loseOrWin(1);
			}
		};
	
	// Update the stage in order:
	configureStage();
    update();
	setInterval(update, 20);
	
}());
